/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SPATIAL_MGR_SERVICE_INNER_H
#define SPATIAL_MGR_SERVICE_INNER_H

#include <functional>
#include <map>
#include <list>
#include <set>

#include <common_event_publish_info.h>
#include <ohos/aafwk/content/want.h>
#include "errors.h"
#include "delayed_sp_singleton.h"
#include "spatial_location_mode.h"
#include "spatial_location_info.h"
#include "ispatial_location_callback.h"
#include "spatial_coordinate_system.h"
#include "spatial_device_info.h"

namespace OHOS {
namespace Msdp {
class SpatialaWarenessMgrService;
class SpatialMgrServiceInner : public SpatialCoordinateSystem::UpdateListener {
public:
    explicit SpatialMgrServiceInner(const wptr<SpatialaWarenessMgrService>& sams);
    SpatialMgrServiceInner() {}
    virtual ~SpatialMgrServiceInner();

    bool Init();
    void SubscribeSpatialLocationRelationship(const SpatialLocationMode& mode,
        const SpatialDeviceInfo &info, const sptr<ISpatialLocationCallback>& callback);
    void UnSubscribeSpatialLocationRelationship(const SpatialLocationMode& mode,
        const SpatialDeviceInfo &info, const sptr<ISpatialLocationCallback> &callback);
    bool GetMonitoringDevices(std::vector<DeviceInfo>& devices);

    class SpatialLocationCallbackDeathRecipient : public IRemoteObject::DeathRecipient {
    public:
        SpatialLocationCallbackDeathRecipient() = default;
        SpatialLocationCallbackDeathRecipient(const SpatialLocationMode& mode,
            const SpatialDeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
            : mode_(mode), info_(info), callback_(callback) {};
        virtual void OnRemoteDied(const wptr<IRemoteObject> &remote);
        virtual ~SpatialLocationCallbackDeathRecipient() = default;
    private:
        SpatialLocationMode mode_;
        SpatialDeviceInfo info_;
        sptr<ISpatialLocationCallback> callback_;
    };
private:
    void NotifyAllListeners(uint32_t mode);
    void NotifyDirectionRes(const DirectionResponse &directionRes);
    void NotifyAngleRes(const AngleResponse &angleRes);
    void NotifyDistanceRes(const DistanceResponse &distanceRes);
    void NotifyInnerOuterRes(const InnerOuterResponse &ioRes);
    DeviceInfo ConvertDeviceInfo(const SpatialDeviceInfo &info);

    void OnDeviceAdded(const DeviceInfo& device, const DeviceCoordinate& coordinate) override;
    void OnDeviceRemoved(const DeviceInfo& device) override;
    void OnDeviceCoordinateUpdate(const DeviceInfo& device, const DeviceCoordinate& coordinate) override;
    void OnUpdateEnd() override;

    class CallbackTrigger {
    public:
        CallbackTrigger(const DeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
            : device_(info), callback_(callback) {};
        CallbackTrigger(const CallbackTrigger& other)
        {
            mode_ = other.mode_;
            device_ = other.device_;
            callback_ = other.callback_;
            coordinate_ = other.coordinate_;
        };
        virtual ~CallbackTrigger() = default;
        SpatialLocationMode GetMode()
        {
            return mode_;
        };
        bool Match(const DeviceInfo &info, const SpatialLocationMode &mode,
            const sptr<ISpatialLocationCallback>& callback);
        bool IsMonitoredDevice(const DeviceInfo &info);
        virtual bool Trigger(const DeviceCoordinate& update) = 0;
    protected:
        SpatialLocationMode mode_;
        DeviceInfo device_;
        sptr<ISpatialLocationCallback> callback_;
        DeviceCoordinate coordinate_;
    };

    class DirectionTrigger : public CallbackTrigger {
    public:
        DirectionTrigger(const DeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
            : CallbackTrigger(info, callback)
        {
            mode_ = SpatialLocationMode::MODE_DIRECTION;
        };
        DirectionTrigger(const DirectionTrigger& other) : CallbackTrigger(other)
        {
            directionRes_ = other.directionRes_;
        };
        virtual ~DirectionTrigger() = default;
        virtual bool Trigger(const DeviceCoordinate& update) override;
    private:
        DirectionResponse directionRes_;
    };

    class DistanceTrigger : public CallbackTrigger {
    public:
        DistanceTrigger(const DeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
            : CallbackTrigger(info, callback)
        {
            mode_ = SpatialLocationMode::MODE_DISTANCE;
        };
        DistanceTrigger(const DistanceTrigger& other) : CallbackTrigger(other)
        {
            distanceRes_ = other.distanceRes_;
        };
        virtual ~DistanceTrigger() = default;
        virtual bool Trigger(const DeviceCoordinate& update) override;
    private:
        DistanceResponse distanceRes_;
    };

    class AngleTrigger : public CallbackTrigger {
    public:
        AngleTrigger(const DeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
            : CallbackTrigger(info, callback)
        {
            mode_ = SpatialLocationMode::MODE_ANGLE;
        };
        AngleTrigger(const AngleTrigger& other) : CallbackTrigger(other)
        {
            angleRes_ = other.angleRes_;
        };
        virtual ~AngleTrigger() = default;
        virtual bool Trigger(const DeviceCoordinate& update) override;
    private:
        AngleResponse angleRes_;
    };

    class InnerOuterTrigger : public CallbackTrigger {
    public:
        InnerOuterTrigger(const DeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
            : CallbackTrigger(info, callback)
        {
            mode_ = SpatialLocationMode::MODE_INNER_OUTER;
        };
        InnerOuterTrigger(const InnerOuterTrigger& other) : CallbackTrigger(other)
        {
            ioRes_ = other.ioRes_;
        };
        virtual ~InnerOuterTrigger() = default;
        virtual bool Trigger(const DeviceCoordinate& update) override;
    private:
        InnerOuterResponse ioRes_;
    };

    class ExistTrigger : public CallbackTrigger {
    public:
        ExistTrigger(const DeviceInfo &info, const sptr<ISpatialLocationCallback>& callback)
            : CallbackTrigger(info, callback)
        {
            mode_ = SpatialLocationMode::MODE_INNER_OUTER;
        };
        ExistTrigger(const ExistTrigger& other) : CallbackTrigger(other)
        {
            existRes_ = other.existRes_;
        };
        virtual ~ExistTrigger() = default;
        virtual bool Trigger(const DeviceCoordinate& update) override;
    private:
        ExistResponse existRes_;
    };

    struct classcomp {
        bool operator()(const sptr<ISpatialLocationCallback> &l, const sptr<ISpatialLocationCallback> &r) const
        {
            return l->AsObject() < r->AsObject();
        }
    };

    std::mutex mutex_;
    const wptr<SpatialaWarenessMgrService> sams_;
    sptr<IRemoteObject::DeathRecipient> spatialLocationCBDeathRecipient_;
    std::set<const sptr<ISpatialLocationCallback>, classcomp> spatialLocationListeners_;
    std::map<SpatialLocationMode, std::set<const sptr<ISpatialLocationCallback>, classcomp>> mSpatialLocationListeners_;
    std::map<DeviceInfo, std::list<std::shared_ptr<CallbackTrigger>>> triggerMap_;
    DirectionResponse directionRes_;
    DistanceResponse distanceRes_;
    AngleResponse angleRes_;
    InnerOuterResponse ioRes_;
    DeviceCoordinate updateCoordinate_;
};
} // namespace Msdp
} // namespace OHOS
#endif // SPATIAL_MGR_SERVICE_INNER_H